﻿/*
 * Copyright (C) 2012, Jesus Requejo <jesus.requejo@gmail.com> 
 * Licensed under Microsoft Public License (Ms-PL)
 * http://www.microsoft.com/en-us/openness/licenses.aspx#MPL
*/

namespace Infrastructure {

   using System;
   using System.Collections.Generic;
   using System.IO;
   using System.Linq;

   public class PermissionsFileSystemStorage {
      private string StorageFolder;
      private string PermissionsFile;
      private static readonly string StartupSectionHeader = "[StartupPermissions]";
      private static readonly string StartupSectionFooter = "[/StartupPermissions]";
      private static readonly string ManagerSectionHeader = "[ServiceManagerPermissions]";
      private static readonly string ManagerSectionFooter = "[/ServiceManagerPermissions]";

      private IDictionary<string, ServiceManagerPermissions> m_managerUserClass
         = new Dictionary<string, ServiceManagerPermissions>() { 
            {ServiceManagerPermissions.Nobody.ToString(), ServiceManagerPermissions.Nobody},
            {ServiceManagerPermissions.Admins.ToString(), ServiceManagerPermissions.Admins},
            {ServiceManagerPermissions.Authorized.ToString(), ServiceManagerPermissions.Authorized}
         };

      public string PermissionsLocation { get { return Path.Combine(StorageFolder, PermissionsFile); } }

      protected PermissionsFileSystemStorage(string StorageFolder, string PermissionsFile) {
         string appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
         this.StorageFolder = Path.Combine(appDataFolder, StorageFolder);
         this.PermissionsFile = PermissionsFile;
      }

      public PermissionsFileSystemStorage() {
         string StorageFolderName = "LoaderService";
         string appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
         this.StorageFolder = Path.Combine(appDataFolder, StorageFolderName);
         this.PermissionsFile = "Permissions.cfg";
      }

      public PermissionsConfiguration ReadPermissionsConfiguration() {

         var configuration = new PermissionsConfiguration();
         FileStream fs = new FileStream(PermissionsLocation, FileMode.Open, FileAccess.Read);
         using (var sr = new StreamReader(fs)) {
            StartupSectionReader.Read(sr, configuration);
            ManagerSectionReader.Read(sr, configuration);
         }

         return configuration;
      }

      public void SavePermissionsConfiguration(PermissionsConfiguration permissions) {
         FileStream fs = new FileStream(PermissionsLocation, FileMode.Create, FileAccess.Write);
         using (var sw = new StreamWriter(fs)) {
            PermissionsWriter.Write(sw, permissions);
         }
      }

      private static class PermissionsWriter {
         public static void Write(StreamWriter sw, PermissionsConfiguration permissions) {
            WriteStartupSection(sw, permissions);
            WriteManagerSection(sw, permissions);
         }

         private static void WriteStartupSection(StreamWriter sw, PermissionsConfiguration permissions) {
            sw.WriteLine(StartupSectionHeader);
            sw.WriteLine(permissions.StartupPermissions.ToString());
            WriteUserList(sw, permissions.AuthorizedStartupUsers);
            sw.WriteLine(StartupSectionFooter);
         }

         private static void WriteManagerSection(StreamWriter sw, PermissionsConfiguration permissions) {
            sw.WriteLine(ManagerSectionHeader);
            sw.WriteLine(permissions.ServiceManagerPermissions.ToString());
            WriteUserList(sw, permissions.AuthorizedServiceManagerUsers);
            sw.WriteLine(ManagerSectionFooter);
         }

         private static void WriteUserList(StreamWriter sw, IList<WindowsUser> users) {
            var userNames = users.Select(u => u.Name);
            foreach (var name in userNames) {
               sw.WriteLine(name);
            }
         }
      }

      private static class StartupSectionReader {

         private static IDictionary<string, StartupPermissions> m_startupUserClass
            = new Dictionary<string, StartupPermissions>() { 
                     {StartupPermissions.Everybody.ToString(), StartupPermissions.Everybody},
                     {StartupPermissions.Admins.ToString(), StartupPermissions.Admins},
                     {StartupPermissions.Authorized.ToString(), StartupPermissions.Authorized}
                  };

         public static void Read(StreamReader sr, PermissionsConfiguration configuration) {
            var header = sr.ReadLine();
            if (!string.Equals(header, StartupSectionHeader, StringComparison.OrdinalIgnoreCase)) {
               throw new InvalidDataException();
            }
            var userClass = sr.ReadLine();
            SetStartupPermissions(userClass, configuration);
            SetAuthorizedUsers(sr, configuration);
         }

         private static void SetStartupPermissions(string userClass, PermissionsConfiguration configuration) {
            if (!m_startupUserClass.ContainsKey(userClass)) {
               throw new InvalidDataException();
            }
            StartupPermissions permissions = m_startupUserClass[userClass];
            configuration.StartupPermissions = permissions;
         }

         private static void SetAuthorizedUsers(StreamReader sr, PermissionsConfiguration configuration) {
            string line;
            int maxLinesCount = 4096;
            int count = 0;
            while (((line = sr.ReadLine()) != null) && !LineIsEndOfSection(line)) {
               count++;
               if (count > maxLinesCount) {
                  // too many lines read
                  throw new InvalidDataException();
               }
               if (ValidLineFormat(line)) {
                  if (configuration.AuthorizedStartupUsers == null) {
                     configuration.AuthorizedStartupUsers = new List<WindowsUser>();
                  }
                  configuration.AuthorizedStartupUsers.Add(new WindowsUser(line, String.Empty));
               }
            }
         }

         private static bool LineIsEndOfSection(string line) {
            return string.Equals(line, StartupSectionFooter, StringComparison.OrdinalIgnoreCase);
         }

         private static bool ValidLineFormat(string line) {
            if (line.Split('\\').Length == 2) {
               return true;
            }
            return false;
         }
      }

      private static class ManagerSectionReader {

         private static IDictionary<string, ServiceManagerPermissions> m_managerUserClass
            = new Dictionary<string, ServiceManagerPermissions>() { 
                     {"Nobody", ServiceManagerPermissions.Nobody},
                     {"Admins", ServiceManagerPermissions.Admins},
                     {"Authorized", ServiceManagerPermissions.Authorized}
                  };

         public static void Read(StreamReader sr, PermissionsConfiguration configuration) {
            var header = sr.ReadLine();
            if (!string.Equals(header, ManagerSectionHeader, StringComparison.OrdinalIgnoreCase)) {
               throw new InvalidDataException();
            }
            var userClass = sr.ReadLine();
            SetPermissions(userClass, configuration);
            SetAuthorizedUsers(sr, configuration);
         }

         private static void SetPermissions(string userClass, PermissionsConfiguration configuration) {
            if (!m_managerUserClass.ContainsKey(userClass)) {
               throw new InvalidDataException();
            }
            ServiceManagerPermissions permissions = m_managerUserClass[userClass];
            configuration.ServiceManagerPermissions = permissions;
         }

         private static void SetAuthorizedUsers(StreamReader sr, PermissionsConfiguration configuration) {
            string line;
            int maxLinesCount = 4096;
            int count = 0;
            while (((line = sr.ReadLine()) != null) && !LineIsEndOfSection(line)) {
               count++;
               if (count > maxLinesCount) {
                  // too many lines read
                  throw new InvalidDataException();
               }
               if (ValidLineFormat(line)) {
                  if (configuration.AuthorizedServiceManagerUsers == null) {
                     configuration.AuthorizedServiceManagerUsers = new List<WindowsUser>();
                  }
                  configuration.AuthorizedServiceManagerUsers.Add(new WindowsUser(line, String.Empty));
               }
            }
         }

         private static bool
            LineIsEndOfSection(string line) {
            return string.Equals(line, ManagerSectionFooter, StringComparison.OrdinalIgnoreCase);
         }

         private static bool ValidLineFormat(string line) {
            if (line.Split('\\').Length == 2) {
               return true;
            }
            return false;
         }
      }
   }
}
